package yao.jdk7.nio;



import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ExecutionException;

import org.junit.Test;

/***
 * NIO
优点：
    1.它完全取代了java.io.File与文件系统的交互
    2.他提供了新的异步处理类，让你无需手动配置线程池和其他底层并发机制，便可在后台线程中执行文件和网络I/O操作
    3.它已入了新的Network-Channel构造方法，简化了Socket与通道的编码工作
文件I/O的操作基石 Path
    Path不一定代表真实的文件或目录。你可以随心所欲地操作Path,用Files中的功能来检查文件是否存在，并对它处理
    Path并不仅限于传统的文件系统，它能表示zip或者jar这样的文件系统


 * @author chenyao
 * @date 2016年10月19日 下午4:10:22
 */
public class NIOTest {

	@Test 
	public void testPath() {
		//获取基本信息
		Path path = Paths.get("d:\\testoss\\1\\2");	//可以相对路径
		System.out.println(path.getParent());
		System.out.println(path.getRoot());
		System.out.println(path.subpath(1, 3));
		
		//合并路径
		Path prefix = Paths.get("d:/uat/");
		Path completePath = prefix.resolve("d:/uat/conf/application.properties");
		System.out.println(completePath);
		
		//两个路径的相对路径
		Path prefix1 = Paths.get("d:/uat/conf/1/2");
		Path completePath1 = prefix1.relativize(prefix);
		System.out.println(completePath1);
		
		//Path与File File 与Path转换
		File f = new File("d:/uat/conf/1/2");
		Path path2 = f.toPath();	
		File file = path2.toFile();	
	}
	
	//catalogue
	/**
	 * 处理目录，目录树
	 * 1.循环遍历目录中的子项，
	 * 2.用glob表达式进行目录子项匹配和基于MIME的内容检测
	 * 3.用walkFileTree方法实现递归移动，复制和删除操作
	 * 
	 * @author chenyao
	 * @throws IOException 
	 * @date 2016年10月19日 上午10:41:21
	 */
	@Test 
	public void testPath1() throws IOException {
		//在目录中查找文件以及在目录树中执行相同的任务
		//1.在目录中查找文件
		/*Path dir = Paths.get("d:/testnio");
		try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {
			for(Path entry : stream) {
				System.out.println(entry.getFileName());
			}
		} catch (IOException e) {
			// TODO: handle exception
			e.printStackTrace();
		}*/
		Path dir = Paths.get("d:/testnio");
		Files.walkFileTree(dir, new FindTextVisitor());
		
		
	}
	
	/**
	 * 文件系统的操作任务
	 * 移动文件，修改文件属性，处理文件内容
	 * 由Files类提供
	 * @throws IOException
	 * @author chenyao
	 * @date 2016年10月19日 上午11:09:42
	 */
	@Test 
	public void testFiles() throws IOException {
		//Files 让你轻松复制移动删除或者处理文件的工具类
		// WatchService 用来监视文件或者目录的核心类，不管它们有没有变化
		//1.创建和删除文件
		Path path = Paths.get("d:/testnio/createDir");
		/*Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
		perms.add(PosixFilePermission.OWNER_READ);
		perms.add(PosixFilePermission.OWNER_WRITE);
		FileAttribute<Set<PosixFilePermission>> attrs = PosixFilePermissions.asFileAttribute(perms);*/
		
		
	}
	
	/**
	 * 快速读写
	 * @throws IOException
	 * @author chenyao
	 * @date 2016年10月19日 下午1:34:29
	 */
	@Test 
	public void testReadAndWrite() throws IOException {
		//打开文件
		Path path = Paths.get("d:/testnio/0.txt");
		//这里选择的编码要正确，不然会抛出 java.nio.charset.MalformedInputException: Input length = 1 异常
		try(BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
			String line;
			while((line = reader.readLine()) != null) {
				System.out.println(line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		//写入文件
		try(BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.WRITE)) {
			writer.append("11111111111");
		} 
		
		
	}
	
	/**
	 * 监测
	 * @throws IOException
	 * @author chenyao
	 * @date 2016年10月19日 下午2:04:06
	 */
	@Test 
	public void testWatchService() throws IOException {
		boolean shutdown = false;
		try {
			Path path = Paths.get("d:/testnio");
			WatchService watcher = FileSystems.getDefault().newWatchService();
			WatchKey key = path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
			while(!shutdown) {
				key = watcher.take();
				for(WatchEvent<?> event : key.pollEvents()) {
					System.out.println(event.kind());
					if(event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
						System.out.println("dir is changed!");
					}
				}
				key.reset();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 异步I/O读写 SeekableByteChannel
	 * SeekableByteChannel 是为了让开发人员能够改变字节通道的位置和大小，比如 应用服务器为了分析日志中的某个错误码，可以让多线程去访问连接在一个大型日志文件上的字节通道
	 * 
	 * @throws IOException
	 * @author chenyao
	 * @date 2016年10月19日 下午2:46:43
	 */
	@Test 
	public void testSeekableByteChannel() throws IOException {
		//FileChannel 可以再文件读取或者写入时保持当前位置
		//读取文件最后1000个字符
		Path logFile = Paths.get("d:/testnio/baobaoapi.log");
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		FileChannel channel = FileChannel.open(logFile, StandardOpenOption.READ);
		channel.read(buffer, channel.size() - 1000);
		System.out.println(new String(buffer.array()));
	}
	
	/**
	 * 异步I/O操作（参考书籍：o reilly java NIO）
	 * NIO2异步能力：这种能力对socket和文件io都适用
	 * 异步IO一种在读写操作结束前允许进行其他操作的IO处理
	 * java7中3个新的异步通道 
	 * 1.AsynchronousFileChannel 用于文件IO
	 * 2.AsynchronousSocketChannel  用于套接字IO，支持超时
	 * 3.AsynchronousServerSocketChannel 用于套接字接受异步链接
	 * 使用两种形式 将来式 回调式
	 * @throws IOException
	 * @author chenyao
	 * @throws ExecutionException 
	 * @throws InterruptedException 
	 * @date 2016年10月19日 下午3:13:20
	 */
	@Test 
	public void testAsyncIO() {
		
		//将来式 future
		// 当希望由主控线程发起IO操作并轮询等待结构时，一般都会用将来式来异步处理
		/*try {
			Path file = Paths.get("d:/testnio/baobaoapi.log");
			AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
			ByteBuffer buffer = ByteBuffer.allocate(100_000);
			Future<Integer> result = channel.read(buffer, 0);
			while(!result.isDone()) {
				System.out.println("is working...");
				//ProfitCalculator.calculateTax();
			}
			Integer bytesRead = result.get();
			System.out.println("Bytes read [" + bytesRead +"]");
		} catch (Exception e) {
			e.printStackTrace();
		}*/
		
		//回调式
		//所采用的时间处理技术类似于在Swing UI编程时采用的机制。
		//其基本思想是主线程会派一个侦查员CompletionHandler 到独立的线程中执行IO操作
		//这个侦查员将带着IO操作的结果返回到主线程中，这个结果会触发它自己的completed或者failed方法（你会重写这两个方法）
		//在异步事件刚一成功或者失败并需要马上行动时，一般会用回调式
		//在异步IO活动结束后，接口CompletionHander 会被调用
		try {
			Path file = Paths.get("d:/testnio/baobaoapi.log");
			AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
			ByteBuffer buffer = ByteBuffer.allocate(100_000);
			channel.read(buffer, 0 , buffer, new CompletionHandler<Integer, ByteBuffer>() {
				@Override
				public void completed(Integer result, ByteBuffer attachment) {
					System.out.println("Bytes read["+ result + "].");
				}
				@Override
				public void failed(Throwable exc, ByteBuffer attachment) {
					System.out.println(exc.getMessage());
				}
				
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * NetworkChannel
	 * 
	 * @author chenyao
	 * @date 2016年10月19日 下午4:00:15
	 */
	@Test 
	public void testNetworkChannel() {
		
	}
	
	
	
	private static class FindTextVisitor extends SimpleFileVisitor<Path> {

		@Override
		public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
			if(file.toString().endsWith(".txt")) {
				System.out.println(file.toAbsolutePath());
			}
			return super.visitFile(file, attrs);
		}
		
	}
	

	
	
	
	
	
	

}
